use crate::libc;

pub struct Vector<T> {
    pub data: *mut T,
    pub size: usize,
    pub capacity: usize,
} 

//=====================================================================//
//                        Create and Destory                           //
//=====================================================================//

pub unsafe fn create<T>() -> *mut Vector<T> {
    let ptr = libc::malloc(size_of::<Vector<T>>());
    let vec = ptr as *mut Vector<T>;
    (*vec).data = core::ptr::null_mut();
    (*vec).size = 0;
    (*vec).capacity = 0;
    vec
}

pub unsafe fn destory<T>(vec: *mut Vector<T>) {
    libc::free((*vec).data as *mut u8);
    libc::free(vec as *mut u8);
}

//=====================================================================//
//                              Push                                   //
//=====================================================================//

pub unsafe fn push<T>(vec: *mut Vector<T>, val: T) {
    if (*vec).size >= (*vec).capacity {
        if (*vec).capacity == 0 {
            (*vec).capacity = 32;
        } else {
            (*vec).capacity *= 2;
        }
        let ptr = (*vec).data as *mut u8;
        let byte_size = size_of::<T>() * (*vec).capacity;
        (*vec).data = libc::realloc(ptr, byte_size) as *mut T;
    }
    *((*vec).data.add((*vec).size)) = val;
    (*vec).size += 1;
}

//=====================================================================//
//                              utils                                  //
//=====================================================================//

pub unsafe fn data<T>(vec: *mut Vector<T>) -> *mut T {
    (*vec).data
}

pub unsafe fn size<T>(vec: *mut Vector<T>) -> usize {
    (*vec).size
}

pub unsafe fn capacity<T>(vec: *mut Vector<T>) -> usize {
    (*vec).capacity
}

pub unsafe fn get<T>(vec: *mut Vector<T>, index: usize) -> *mut T {
    let data = data(vec);
    data.add(index)
}

#[allow(unused)]
#[cfg(test)]
mod test {
    #[test]
     fn test_push() {
        unsafe {
            let vals = [1, 12, 32, -31];
            let vec = super::create::<i32>();
            for val in vals {
                super::push(vec, val);
            }

            for i in 0..super::size(vec) {
                assert_eq!(vals[i], *super::get(vec, i));
            }

            super::destory(vec);
        }
    }
}